///\file housegl.cpp
///\brief contient les implémentations des méthodes de HouseGL
///\author Allan BLANCHARD
#include "housegl.hh"
#include <unistd.h>
#include <cmath>

HouseGL::HouseGL(unsigned int width, unsigned int height, const char* name)
  : GLApplication(width, height, name), 
    lastmousex(w/2), lastmousey(h/2),
    debout(0.5),
    eyex(-1), eyez(0),
    phi(315), psi(0), repere(false),
    ecartement(0.0)
{
  glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
  glClearDepth(1.0);
  
  soleil.setAmb(0.3, 0.3, 0.1);
  soleil.setSpe(.4, .4, .4);
  soleil.setDif(0.4, 0.4, 0.2);

  lumiere.setAmb(0.5, 0.5, 0.5);
  lumiere.setSpe(0., 0., 0.);
  lumiere.setDif(0.6, 0.6, 0.6);

  lampe.setAmb(0.3, 0.15, 0.);
  lampe.setSpe(.2, .2, 0);
  lampe.setDif(0.4, 0.2, 0.);

  glutWarpPointer(w/2, h/2);
  glutSetCursor(GLUT_CURSOR_NONE);

  glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,1);
  glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER,1);
  
  glEnable(GL_LIGHTING);
  glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

  glShadeModel(GL_SMOOTH);
  
  glEnable(GL_MULTISAMPLE);
  glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}

void HouseGL::display(){
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  float dist = (xmax+ymax+zmax)/3;
  float xz   = dist*cos(psi*M_PI/180);

  if(repere){
    glDisable(GL_LIGHTING);
    
    glBegin(GL_LINES);
    glColor3f(1.,0.,0.);
    glVertex3f(100.,0.,0.);
    glVertex3f(0.,0.,0.);
    glColor3f(0.,1.,0.);
    glVertex3f(0.,100.,0.);
    glVertex3f(0.,0.,0.);
    glColor3f(0.,0.,1.);
    glVertex3f(0.,0.,100.);
    glVertex3f(0.,0.,0.);
    glEnd();
    glColor3f(1.,1.,1.);

    glPushMatrix();
    glTranslatef(xmax, 3*ymax/2, zmax);
    glutWireSphere((ymax-ymin)/20, 10, 10);
    glPopMatrix();
  
    glPushMatrix();
    glTranslatef(xmin, ymax/2, zmin);
    glutWireSphere((ymax-ymin)/20, 10, 10);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(xmin*0.25, 17*ymax/11, zmax*0.8);
    glutWireSphere((ymax-ymin)/20, 10, 10);
    glPopMatrix();

    glEnable(GL_LIGHTING);
  }

  lumiere.placeAt(xmax, 3*ymax/2, zmax, 1.);
  soleil.placeAt(xmin, ymax/2, zmin, 1.);
  lampe.placeAt(xmin*0.25, 17*ymax/11, zmax*0.8, 1.);

  lumiere.activate();
  soleil.activate();
  lampe.activate();
  
  float ax = eyex+xz*(cos(phi*M_PI/180));
  float ay = debout*ymax+dist*sin(psi*M_PI/180);
  float az = eyez+xz*(sin(phi*M_PI/180));

  camera.setfocuspos(ax, ay, az);
  camera.sethdirpos(0,1,0);
  
  float norme = sqrt(az*az + ax*ax);

  //(az, 0, ax) correspond à (vecteurcamera v hdir)
  camera.seteyepos(eyex + az/norme*ecartement ,
		   debout*ymax,
		   eyez + ax/norme*ecartement);
  camera.place(); 

  if(ecartement)
    glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
  for(unsigned int i(0); i < models.size(); i++)
    models[i]->draw();

  if(ecartement){
    glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
    glLoadIdentity();

    //(az, 0, ax) correspond à (vecteurcamera v hdir)
    camera.seteyepos(eyex - az/norme*ecartement ,
		     debout*ymax,
		     eyez - ax/norme*ecartement);
    camera.place(); 
    
    glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
    glClear(GL_DEPTH_BUFFER_BIT);
    for(unsigned int i(0); i < models.size(); i++)
      models[i]->draw();
    
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  }

  glutSwapBuffers();
}

void HouseGL::idle(){
  usleep(100);
  framerate();
  display();
}

void HouseGL::keynormal(unsigned char key, int x, int y){
  switch(key){
  case 'q' : {
    eyez -= cos((phi*M_PI)/180)*0.05;
    eyex += sin((phi*M_PI)/180)*0.05;
  } break;
  case 'd' : {
    eyez += cos((phi*M_PI)/180)*0.05;
    eyex -= sin((phi*M_PI)/180)*0.05;
  } break;
  case 'z' : {
    eyez += sin((phi*M_PI)/180)*0.05;
    eyex += cos((phi*M_PI)/180)*0.05;
  } break;
  case 's' : {
    eyez -= sin((phi*M_PI)/180)*0.05;
    eyex -= cos((phi*M_PI)/180)*0.05;
  } break;
  case '0' : {
    repere = !repere; 
    if(repere) glutSetCursor(GLUT_CURSOR_INHERIT);
    else       glutSetCursor(GLUT_CURSOR_NONE);
  } break;
  case '+' : {
    ecartement += 0.005;
  } break;
  case '-' : {
    ecartement -= 0.005;
  } break;
  case 0x1B : ::exit(1); break;
  }

  if(eyez < zmin) eyez = zmin+0.01;
  if(eyez > zmax) eyez = zmax-0.01;
  if(eyex < xmin) eyex = xmin+0.01;
  if(eyex > xmax) eyex = xmax-0.01;
}

void HouseGL::keyspecial(int key, int x, int y){
  
}

void HouseGL::mouseclick(int b, int e, int x, int y){
  switch(b){
  case 3 : {
    if(debout < 1.)
      debout += 0.05*((eyex > 0.4*xmax) || (debout < 0.95));
    else
      debout += 0.05;
  } break;
  case 4 : {
    if(debout > 1.)
      debout -= 0.05*((eyex > 0.4*xmax) || (debout > 1.05));
    else
      debout -= 0.05;
  } break;
  default : break;
  }
  if(debout > 1.95) debout = 1.95;
  else if(debout < 0.05) debout = 0.05;
}

void HouseGL::mousemot(int x, int y){
  passivemousemotion(x,y);
}

void HouseGL::passivemousemotion(int x, int y){
  if(x == static_cast<int>(w/2) && y == static_cast<int>(h/2)){
    lastmousex = x;
    lastmousey = y;
  } else {
    glutWarpPointer(w/2, h/2);
    
    phi += static_cast<float>(x-lastmousex)*110/w;    
    if(phi < 0) phi = 359;
    else if(phi > 360) phi = 1;

    psi -= static_cast<float>(y-lastmousey)*110/h;
    if(psi < -89) psi = -89;
    else if(psi > 89) psi = 89;

    lastmousex = x;
    lastmousey = y;
  }
}

void HouseGL::addModel(Model& m, bool ignore){
  if(!ignore){
    if(models.size() == 0){
      xmin = m.getxmin();
      xmax = m.getxmax();
      ymin = m.getymin();
      ymax = m.getymax();
      zmin = m.getzmin();
      zmax = m.getzmax();
    }
    else{
      if(m.getxmax() > xmax)
	xmax = m.getxmax();
      if(m.getxmin() < xmin)
	xmin = m.getxmin();
      
      if(m.getymax() > ymax)
	ymax = m.getymax();
      if(m.getymin() < ymin)
	ymin = m.getymin();
      
      if(m.getzmax() > zmax)
	zmax = m.getzmax();
      if(m.getzmin() < zmin)
	zmin = m.getzmin();
    }
  }

  eyex = 0.8*xmin;
  eyez = 0.8*zmax;

  m.toVBO();
  models.push_back(&m);
}

void HouseGL::exit(){
  for(unsigned int i(0); i < models.size(); i++)
    models[i]->free();
}
